<template>
  <div style="border: 1px solid #ccc; width: 100%">
    <Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="toolbarMode" />
    <Editor
      :style="`height: ${editorHeight}px; overflow-y: hidden`"
      v-model="content"
      :defaultConfig="editorConfig"
      :mode="editorMode"
      @onCreated="handleCreated"
    />
  </div>
</template>

<script setup lang="ts">
//引入wangeditor富文本编辑器
import '@wangeditor/editor/dist/css/style.css'; // 引入 css
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
import { onBeforeUnmount, shallowRef, watch } from 'vue';
import urls from '@/api/request/urls';
import Request from '@/api/request/request';
import { useAuthStore } from '@/stores/auth';
import type { ResponseData } from '@/types/interface/base_interface';
import type { IToolbarConfig } from '@wangeditor/editor';

// 接收父组件的值
interface Props {
  toolbarMode?: string; //工具栏模式。mode: 'default' 默认模式 - 集成了 wangEditor 所有功能；mode: 'simple' 简洁模式 - 仅有部分常见功能，但更加简洁易用
  editorMode?: string; //编辑器模式，
  editorHeight?: number; //编辑器高度
  editorPlaceholder?: string; //编辑器placeholder
  toolbarExcludeKeys?: string[]; //编辑器工具栏要排除的按钮或菜单，要参考wangeditor
}
const props = withDefaults(defineProps<Props>(), {
  toolbarMode: 'default',
  editorMode: 'default',
  editorHeight: 500,
  editorPlaceholder: '请输入内容',
  toolbarExcludeKeys: () => []
});
//接收表单标题
const content = defineModel<string>({
  required: false,
  default: ''
});
//下面的watch是用来调试的。注意，content的值可以为undefinded但是不能为Null，否则会出错。
// watch(
//   () => content.value,
//   (newVal: string | undefined, oldVal: string | undefined) => {
//     // content.value = '<p>12<br></p>';
//     console.log('新值：' + newVal);
//     console.log('旧值' + oldVal);
//   },
//   { deep: true, immediate: true }
// );
//20250818修复添加下述代码：当父组件清空 content时，有时候（主要是第一次编辑，然后再添加的时候）编辑器DOM可能还未完成渲染更新，导致旧值残留
//目的：监听content变化并同步

watch(
  () => content.value,
  (newVal) => {
    const editor = editorRef.value;
    if (editor && newVal === '') {
      editor.clear();
    }
  },
  { immediate: true }
);

const authStore = useAuthStore();
//富文本编辑器
// 编辑器实例，必须用 shallowRef
const editorRef = shallowRef();
// 内容 HTML
// const valueHtml = ref('<p>hello</p>');
//工具栏配置
const toolbarConfig: Partial<IToolbarConfig> = {
  //要排除的工具栏
  excludeKeys: props.toolbarExcludeKeys
};
const editorConfig = {
  MENU_CONF: { uploadImage: {} },
  placeholder: props.editorPlaceholder
};
//wangeditor官方文档：如果用于 Typescript ，则要定义插入函数的类型。
type InsertFnType = (url: string, alt: string, href: string) => void;
//图片上传
editorConfig.MENU_CONF['uploadImage'] = {
  //使用自定义上传动作后，不需要单独再封装参数
  // server: `${urls.BASEURL}/api/backstage/system/file/upload`,
  // //   fieldName: 'file', //对应后端的文件参数
  // meta: {
  //   dirName: 'image'
  // },
  // headers: {
  //   access_token: authStore.getToken()
  // },
  //自定义上传动作，执行之后就不需要下面的其他onBeforeUpload，onProgress，……等函数
  async customUpload(file: File, insertFn: InsertFnType) {
    //封装上传文件、其他参数
    const formData = new FormData();
    formData.append('file', file);
    formData.append('dirName', 'image');
    //传递文件到服务器
    const response: ResponseData = await new Request(`${urls.API_URL_BACKSTAGE_FILERESOURCE_UPLOAD}`, formData, {
      headers: {
        'Content-Type': 'multipart/form-data' // 设置请求头为multipart/form-data
      }
    }).post();
    if (response.success) {
      //如果成功则插入图片到编辑器
      insertFn(response.data.fileUrl, response.data.fileName, response.data.fileUrl); // 插入图片，参数：图片 src、alt、href
    }
  },
  // 自定义插入图片动作。如果自定义服务端返回值，需要编写，一般都需要。
  // customInsert(res: any, insertFn: InsertFnType) {
  //   if (res.success) {
  //     // "url": "xxx", // 图片 src ，必须
  //     // "alt": "yyy", // 图片描述文字，非必须
  //     // "href": "zzz" // 图片的链接，非必须
  //     // 从 res 中找到 url alt href ，然后插入图片
  //     insertFn(res.data.fileUrl, res.data.fileName, res.data.fileUrl);
  //   }
  // },
  // 上传之前触发
  onBeforeUpload(file: File) {
    // TS 语法
    // onBeforeUpload(file) {    // JS 语法
    // file 选中的文件，格式如 { key: file }
    return file;

    // 可以 return
    // 1. return file 或者 new 一个 file ，接下来将上传
    // 2. return false ，不上传这个 file
  },

  // 上传进度的回调函数
  onProgress(progress: number) {
    // TS 语法
    // onProgress(progress) {       // JS 语法
    // progress 是 0-100 的数字
    console.log('progress', progress);
  },

  // 单个文件上传成功之后
  onSuccess(file: File, res: any) {
    // TS 语法
    // onSuccess(file, res) {          // JS 语法
    console.log(`${file.name} 上传成功`, res);
  },

  // 单个文件上传失败
  onFailed(file: File, res: any) {
    // TS 语法
    // onFailed(file, res) {           // JS 语法
    console.log(`${file.name} 上传失败`, res);
  },

  // 上传错误，或者触发 timeout 超时
  onError(file: File, err: any, res: any) {
    // TS 语法
    // onError(file, err, res) {               // JS 语法
    console.log(`${file.name} 上传出错`, err, res);
  }
};

// 组件销毁时，也及时销毁编辑器
onBeforeUnmount(() => {
  const editor = editorRef.value;
  if (editor == null) return;
  editor.destroy();
});

const handleCreated = (editor: any) => {
  editorRef.value = editor; // 记录 editor 实例，重要！
};

// 添加重置方法:当父组件清空 content时，有时候（主要是第一次编辑，然后再添加的时候）编辑器DOM可能还未完成渲染更新，导致旧值残留
// const resetEditor = () => {
//   const editor = editorRef.value;
//   if (editor) {
//     editor.clear(); // 官方API，清空内容
//     // 或手动设置
//     editor.setHtml('');
//   }
// };
// defineExpose({ resetEditor });
//
</script>

<style scoped></style>
